#pragma once

#include "Types.h"
#include "ResourceString.h"
#include "DLL.h"

#include <MyFC/Event.h>
#include <MyFC/Widget.h>
#include <map>
#include <list>
#include <stdexcept>
#include <xml/node.h>

namespace My {

class PropertyBase;
class EditableObject;

class DLL_API EditableList : public std::list<EditableObject *> {
public:
	EditableList();
	EditableList(Size count, EditableObject * pValue);
public:
	virtual void Resize(Size);
};

typedef std::map<ResourceString, PropertyBase *>	PropertyMap;

class DLL_API PropertyBase : public EventDispatcher {
public:
	virtual ~PropertyBase() { }
public:
	/// Default = ""
	virtual String GetHint(const EditableList &) const;
	/// Default = false
	virtual bool ReadOnly(const EditableList &) const;
	/// Default = true
	virtual bool Visible(const EditableList &) const;
	/// Default = 0
	virtual const PropertyMap * GetSubProperties(const EditableList &) const;
	/// Returns a list of objects to which this property applies
	virtual void GetObjects(const EditableList & in, EditableList & out) const;
	/// Return true for small subproperty lists
	virtual bool DefaultExpanded() const;
	/// Return true if this property is applicable to given object
	virtual bool Accept(const EditableObject &) const = 0;
	/// Default deserializes subproperties
	virtual void Deserialize(xml::const_node const &, EditableObject &) const;
	/// Default serializes subproperties
	virtual void Serialize(xml::node const &, const EditableObject &) const;
	/// Default returns true if and only if the property has at least one serializable subproperty
	virtual bool Serializable(const EditableList &) const;
public:
	virtual Widget * CreateControl(const EditableList &);
	virtual void FreeControl(Widget *);
};

// methods of typedef PropertyMap
void DLL_API Deserialize(PropertyMap const &, xml::const_node const &, EditableObject &);
void DLL_API Serialize(PropertyMap const &, xml::node const &, const EditableObject &);

/**
 * Ultimate ancestor of PropertyMap classes with
 * trivial initialization.
 */
class DLL_API PropertyMapClassBase : public PropertyMap {
public:
	PropertyMapClassBase(); 
	PropertyMapClassBase & Init();
	bool Initialized() const;
private:
	bool m_Init;
};

class DLL_API EditableObject {
public:
	typedef PropertyMapClassBase PropertyMapClass;
public:
	virtual const PropertyMap & GetProperties() const = 0;
	virtual const char * GetEditCaption() const = 0;
	// Used in PropertyDialog
	virtual void Validate() { }
public:
	void Deserialize(xml::const_node const & n, EditableObject & obj) const
		{ My::Deserialize(GetProperties(), n, obj); }
	void Serialize(xml::node const & n, const EditableObject & obj) const
		{ My::Serialize(GetProperties(), n, obj); }
public:
	void UpdateProperty(PropertyBase * p = 0);
};

class PropertyChange : public My::Event  {
public:
	EditableObject &		m_Object;
	PropertyBase *			m_pProperty;
public:
	PropertyChange(EditableObject & obj, PropertyBase * p)
		: m_Object(obj), m_pProperty(p) { }
};

/**
 * The main trick: 
 * If you want your class A to be editable,
 * then declare it like this:
 *		class A : public Editable<A> { ... }
 * If you want a subclass of A (call it B) to inherit some
 * properties of A, then declare it like this:
 *		class B : public Editable<B, A> { ... }
 */
template <class T, class B = EditableObject>
class Editable : public B {
protected:
	class PropertyMapClass : public B::PropertyMapClass {
	public:
		PropertyMapClass & Init();	
	};
	static PropertyMapClass	theProperties;
public:
	virtual const PropertyMap & GetProperties() const; 
};

class InvalidPropertyValueError : public std::runtime_error {
public:
	InvalidPropertyValueError();
};

} // My

#include "Impl/Property.inl"


